/*
 * Galaxium Messenger
 * Copyright (C) 2007 Paul Burton <paulburton89@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.IO;

namespace Galaxium.Protocol.Msn
{
	public class P2PBinaryHeader
	{
		uint _sessionID = 0;
		uint _messageID = 0;
		ulong _chunkOffset = 0;
		ulong _totalSize = 0;
		uint _chunkSize = 0;
		P2PHeaderFlag _flags = 0;
		uint _ackID = 0;
		uint _ackUID = 0;
		ulong _ackSize = 0;
		
		static Random _random = new Random ();
		
		public uint SessionID
		{
			get	{ return _sessionID; }
			set { _sessionID = value; }
		}
		
		public uint MessageID
		{
			get { return _messageID; }
			set { _messageID = value; }
		}
				public ulong ChunkOffset 
		{
			get { return _chunkOffset; }
			set { _chunkOffset = value; }
		}

		public ulong TotalSize 
		{
			get { return _totalSize; } 
			set { _totalSize = value; } 
		}
		
		public uint ChunkSize 
		{ 
			get { return _chunkSize; } 
			set { _chunkSize = value; } 
		}
		
		public P2PHeaderFlag Flags
		{
			get { return _flags; } 
			set { _flags = value; } 
		}
		
		public uint AckID
		{
			get { return _ackID; }
			set { _ackID = value; }
		}
		
		public uint AckUID
		{
			get { return _ackUID; }
			set { _ackUID = value; }
		}

		public ulong AckSize
		{
			get { return _ackSize; }
			set { _ackSize = value; }
		}

		public P2PBinaryHeader ()
		{
			_ackID = (uint)_random.Next (10000, int.MaxValue);
		}
		
		public P2PBinaryHeader (P2PBinaryHeader copyFrom)
		{
			_ackUID = copyFrom.AckUID;
			_ackID = copyFrom.AckID;
			_ackSize = copyFrom.AckSize;
			_flags = copyFrom.Flags;
			_messageID = copyFrom.MessageID;
			_chunkSize = copyFrom.ChunkSize;
			_chunkOffset = copyFrom.ChunkOffset;
			_sessionID = copyFrom.SessionID;
			_totalSize = copyFrom.TotalSize;
		}
		
		public P2PBinaryHeader (byte[] data)
		{
			MemoryStream stream = new MemoryStream (data);
			BinaryReader reader = new BinaryReader (stream);

			_sessionID = ToLittleEndian (reader.ReadUInt32 ());
			_messageID = ToLittleEndian (reader.ReadUInt32 ());
			_chunkOffset = ToLittleEndian (reader.ReadUInt64 ());
			_totalSize = ToLittleEndian (reader.ReadUInt64 ());
			_chunkSize = ToLittleEndian (reader.ReadUInt32 ());
			_flags = (P2PHeaderFlag)ToLittleEndian (reader.ReadUInt32 ());
			_ackID = ToLittleEndian (reader.ReadUInt32 ());
			_ackUID = ToLittleEndian (reader.ReadUInt32 ());
			_ackSize = ToLittleEndian (reader.ReadUInt64 ());
			
			reader.Close ();
			stream.Close ();
		}
		
		public byte[] ToByteArray ()
		{
			byte[] data = new byte[48];
			MemoryStream stream = new MemoryStream (data);
			BinaryWriter writer = new BinaryWriter (stream);
			
			writer.Write (ToLittleEndian (_sessionID));
			writer.Write (ToLittleEndian (_messageID));
			writer.Write (ToLittleEndian (_chunkOffset));
			writer.Write (ToLittleEndian (_totalSize));
			writer.Write (ToLittleEndian (_chunkSize));
			writer.Write (ToLittleEndian ((uint)_flags));
			writer.Write (ToLittleEndian (_ackID));
			writer.Write (ToLittleEndian (_ackUID));
			writer.Write (ToLittleEndian (_ackSize));
			
			writer.Close ();
			stream.Close ();
			
			return data;
		}
		
		internal static uint ToLittleEndian (uint val)
		{
			if (BitConverter.IsLittleEndian)
				return val;
			
			return FlipEndian (val);
		}
		
		internal static ulong ToLittleEndian (ulong val)
		{
			if (BitConverter.IsLittleEndian)
				return val;
			
			return FlipEndian (val);
		}
		
		internal static uint ToBigEndian (uint val)
		{
			if (!BitConverter.IsLittleEndian)
				return val;
			
			return FlipEndian (val);
		}
		
		internal static ulong ToBigEndian (ulong val)
		{
			if (!BitConverter.IsLittleEndian)
				return val;
			
			return FlipEndian (val);
		}
		
		internal static uint FlipEndian (uint val)
		{
			return (uint)
				 (((val & 0x000000ff) << 24) +
				 ((val & 0x0000ff00) << 8) +
				 ((val & 0x00ff0000) >> 8) +
				 ((val & 0xff000000) >> 24));
		}
		
		internal static ulong FlipEndian (ulong val)
		{
			return (ulong)
				 (((val & 0x00000000000000ff) << 56) +
				 ((val & 0x000000000000ff00) << 40) +
				 ((val & 0x0000000000ff0000) << 24) +
				 ((val & 0x00000000ff000000) <<  8) +
				 ((val & 0x000000ff00000000) >>  8) +
				 ((val & 0x0000ff0000000000) >> 24) +
				 ((val & 0x00ff000000000000) >> 40) +
				 ((val & 0xff00000000000000) >> 56));
		}
	}
}
